#pragma once
#include "Headerfile.hpp"
#include "Util.hpp"

namespace shw
{
#define MAX_SIZE 10*1024*1024
    class SinkLog
    {
    public:
        using ptr = std::shared_ptr<shw::SinkLog>;
        virtual ~SinkLog() {}
        virtual void Log(const char *data, size_t len) = 0;
    };
    // 落地输出到标准显示器
    class StoutSink : public SinkLog
    {
    public:
        using ptr = std::shared_ptr<shw::StoutSink>;
        void Log(const char *data, size_t len) override
        {
            cout.write(data, len);
        }
    };
    // 落地输出到指定文件
    class FileSink : public SinkLog
    {
    public:
        using ptr = std::shared_ptr<FileSink>;

        FileSink(const std::string &path) : _path(path)
        {
            // 文件名不存在则创建
            shw::util::file::CreateDirectory(shw::util::file::Path(_path));
            _ofs.open(_path, std::ios::binary | std::ios::app);
            assert(_ofs.is_open()); // 打开失败
        }
        void Log(const char *data, size_t len) override
        {
            _ofs.write(data, len);
            if (!_ofs.good())
            {
                cout << "写入文件失败!\n";
            }
        }

    private:
        std::string _path;
        std::ofstream _ofs;
    };
    // 基于文件大小输出到滚动文件中
    class RollBysizeSink : public SinkLog
    {
    public:
        using ptr = std::shared_ptr<RollBysizeSink>;
        RollBysizeSink(const std::string &base, size_t maxSize=MAX_SIZE)
            : _base(base), _maxSize(maxSize), _curSize(0), _count(0)
        {
            shw::util::file::CreateDirectory(util::file::Path(_base)); // 创建目录
            _ofs.open(createFilename(), std::ios::binary | std::ios::app);
            assert(_ofs.is_open()); // 打开文件失败
        }
        void Log(const char *data, size_t len) override
        {
            if (_curSize >= _maxSize) // 超出文件大小需要切换新文件
            {
                _ofs.close(); //
                _ofs.open(createFilename(), std::ios::binary | std::ios::app);
                assert(_ofs.is_open()); // 打开文件失败
                _curSize = 0;
            }
            _ofs.write(data, len);
            if (!_ofs.good())
            {
                cout << "写入文件失败！\n";
            }
            _curSize += len;
        }

    private:
        std::string createFilename()
        {
            std::stringstream ss;
            auto time = shw::util::date::Now();
            struct tm lt;
            localtime_r(&time, &lt);
            ss << _base;
            ss << lt.tm_year + 1900;
            ss << lt.tm_mon + 1;
            ss << lt.tm_mday;
            ss << lt.tm_hour;
            ss << lt.tm_min;
            ss << lt.tm_sec;
            ss << '-';
            ss << ++_count;
            ss << ".log";
            return ss.str();
        }

    private:
        std::string _base; // 滚动文件的基础名字，后面可以加上时间等组成文件名
        std::ofstream _ofs;
        size_t _maxSize;
        size_t _curSize;
        size_t _count;
    };
    // 基于时间的滚动文件
    class RollBytimeSink : public SinkLog
    {
    public:
        RollBytimeSink(const std::string &baseName, size_t gapTime = 10 * 60 * 60)
            : _gapTime(gapTime), _baseName(baseName), _curTime(shw::util::date::Now())
        {
            shw::util::file::CreateDirectory(shw::util::file::Path(baseName)); // 创建目录
            std::string filename = createFilename();
            _ofs.open(filename, std::ios::binary | std::ios::app); // 打开文件
            assert(_ofs.is_open());                                // 打开失败，直接退出
        }
        void Log(const char *data, size_t len) override
        {
            auto tm = shw::util::date::Now();
            if (tm >= _curTime + _gapTime)
            {
                _ofs.close(); // 创建新文件之前，关闭之前的
                std::string filename = createFilename();
                _ofs.open(filename, std::ios::binary | std::ios::app);
                assert(_ofs.is_open()); // 打开失败，直接退出
                _curTime = tm;
            }
            _ofs.write(data, len);
            if (!_ofs.good())
            {
                cout << "写入文件失败！\n";
            }
        }
        std::string createFilename()
        {
            std::stringstream ss;
            auto time = shw::util::date::Now();
            struct tm lt;
            localtime_r(&time, &lt);
            ss << _baseName;
            ss << lt.tm_year + 1900;
            ss << lt.tm_mon + 1;
            ss << lt.tm_mday;
            ss << lt.tm_hour;
            ss << lt.tm_min;
            ss << lt.tm_sec;
            ss << '-';
            ss << ++_count;
            ss << ".log";
            return ss.str();
        }

    private:
        size_t _gapTime; // 时间间隔
        std::ofstream _ofs;
        std::string _baseName; // 基础文件名
        time_t _curTime;       // 当前文件创建时间
        size_t _count;
    };
    class SinkFactory
    {
    public:
        template <typename SinkType, typename ...Args>
        static shw::SinkLog::ptr CreateSink(Args&& ...args)
        {
            return std::make_shared<SinkType>(std::forward<Args>(args)...);
        }
    };
}